package rkw.communication;

import java.net.BindException;
import java.util.HashMap;

import org.apache.log4j.Logger;

import rkw.ModulesManager;
import core.communication.Client;
import core.communication.ClientChecker;
import core.communication.Direction;
import core.database.model.NodeExtended;

public class CommunicationManager extends core.managers.CommunicationManager<NodeExtended, ModulesManager, MessageReceiver, MessageSender, InvocationHandler> {
	private static CommunicationManager instance = null;

	private HashMap<String, Client> okws = new HashMap<>();
	private HashMap<String, Client> terminals = new HashMap<>();

	public static CommunicationManager getInstance() throws BindException {
		if (instance == null)
			synchronized (CommunicationManager.class) {
				if (instance == null)
					instance = new CommunicationManager();
			}
		return instance;
	}

	private CommunicationManager() throws BindException {
		logger = Logger.getLogger("DEBUG");
		modulesManager = ModulesManager.getInstance();
		initializeNodes();
		invocationHandler = new InvocationHandler(this);
		setupServer(invocationHandler);
		initializeMessages();
		initializeClientChecker();
	}

	@Override
	protected void initializeMessagsThreads() {
		messageReceiver = new MessageReceiver("receiver", this);
		messageSender = new MessageSender("sender", this);
	}
	
	@Override
	protected void initializeClientChecker() {
		clientChecker = new ClientChecker<CommunicationManager>("clientChceker", this);
	}

	@Override
	protected void initializeNodes() {
		this.mainNode = modulesManager.getMainNode();
		prefferedParentURI = mainNode.getPrefferedParentURI();
		mainNode = modulesManager.getMainNode();
		for (NodeExtended node : modulesManager.getNodesForDirection(Direction.UP))
			okws.put(node.getLocatorURI(), new Client(node.getLocatorURI(), node.getNodeType()));
		for (NodeExtended node : modulesManager.getNodesForDirection(Direction.DOWN))
			terminals.put(node.getLocatorURI(), new Client(node.getLocatorURI(), node.getNodeType()));
	}

	@Override
	public HashMap<String, Client> getClients(Direction direction) {
		if (direction == Direction.UP)
			return okws;
		else
			return terminals;
	}

	@Override
	protected void addTopClient(String locatorUri, Client client) {
		okws.put(locatorUri, client);
	}

	@Override
	protected void addDownClient(String locatorUri, Client client) {
		terminals.put(locatorUri, client);
	}

	@Override
	public void deleteClient(String locatorUri) {
		Client client = null;
		if (okws.containsKey(locatorUri)) {
			client = okws.get(locatorUri);
			okws.remove(locatorUri);
		} else if (terminals.containsKey(locatorUri)) {
			client = terminals.get(locatorUri);
			terminals.remove(locatorUri);
		}
		if (client != null) {
			removeClientToCheck(client);
			client.getClient().disconnect();
		}
		modulesManager.deleteNode(locatorUri);
	}
	
	@Override
	public void editNode(String locatorUri, NodeExtended node) {
		if (locatorUri.compareTo(node.getLocatorURI()) != 0) {
			Client client = okws.get(locatorUri);
			if (client != null) {
				client.getClient().disconnect();
				okws.remove(locatorUri);
				okws.put(node.getLocatorURI(), new Client(node.getLocatorURI(), node.getNodeType()));
			}
		}
		modulesManager.updateNode(locatorUri, node);
	}

	// public void sendConfigs() {
	// LoggerModule.getInstance().log(Level.CONFIG, "CM.sendConfigs: ");
	// ConfigurationMessage message;
	// Object[] clients = terminals.values().toArray();
	// for (Object c : clients) {
	// message = new ConfigurationMessage();
	// message.setDestinationURI(((Client)c).getLocatorURI());
	// message.setPrefferedParentURI(getServerURI());
	// message.setDirection(Direction.DOWN);
	// LoggerModule.getInstance().log(Level.CONFIG,
	// "CM.sendConfigs: Wysylam");
	// send(message);
	// }
	// }

	// TODO: metoda testowa
	// public void sendDown(TextMessage message) {
	// Object[] clients = terminals.values().toArray();
	// TextMessage m;
	// for (Object c : clients) {
	// m = new TextMessage();
	// m.setDirection(message.getDirection());
	// m.setText(message.getText());
	// m.setDestinationURI(((Client)c).getLocatorURI());
	// send(m);
	// }
	// }

}
